#define _CRT_SECURE_NO_WARNINGS
#define STB_IMAGE_IMPLEMENTATION

#include <ciallo_imgui/image.h>
#include <cstdio>
#include <filesystem>
#include <glfw/glfw3.h>
#include <imgui/imgui.h>
#include <iostream>
#include <stb/stb_image.h>

namespace fs = std::filesystem;
std::optional<ciallo::Texture> ciallo::Texture::load(const char *file_name) {

    // ciallo::Texture texture;
    if (fs::exists(file_name)) {
        unsigned int texture;
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        // set the texture wrapping/filtering options (on the currently bound
        // texture object)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                        GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        // load and generate the texture
        int width, height, nrChannels;
        unsigned char *data =
            stbi_load(file_name, &width, &height, &nrChannels, 0);

        GLint format = nrChannels == 3 ? GL_RGB : GL_RGBA;
        if (data) {
            glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format,
                         GL_UNSIGNED_BYTE, data);
        } else {
            std::cout << "Failed to load texture" << std::endl;
        }
        stbi_image_free(data);

        ciallo::Texture ctexture;
        ctexture._id = texture;
        ctexture._width = width;
        ctexture._height = height;
        return ctexture;
    }
    return std::nullopt;
}

ciallo::Texture::~Texture() {
    if (_id != 0) {
        glDeleteTextures(1, &_id);
    }
    return;
}
constexpr int GetGLFormat(ciallo::Format format) {
    switch (format) {
    case ciallo::Format::RGBA:
        return GL_RGBA;
    case ciallo::Format::RGB:
        return GL_RGB;
    }
    return 0;
}
ciallo::Texture::Texture(int width, int height, Format format)
    : _width(width), _height(height) {
    _format = format;
    switch (format) {
    case Format::RGBA:
        _channels = 4;
        break;
    case Format::RGB:
        _channels = 3;
        break;
    default:
        throw std::runtime_error("Invalid texture format, must be RGBA or RGB");
    }

    _data.resize(width * height * _channels);
    printf("Texture created with size %d x %d\n", width, height);
    printf("Texture created with channels %d\n", _channels);
    glGenTextures(1, &_id);
    glBindTexture(GL_TEXTURE_2D, _id);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, GetGLFormat(_format), width, height, 0,
                 GetGLFormat(_format), GL_UNSIGNED_BYTE, NULL);

    glBindTexture(GL_TEXTURE_2D, 0); // unbind
}

void ciallo::Texture::update_texture() {

    glBindTexture(GL_TEXTURE_2D, _id);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _width, _height,
                    GetGLFormat(_format), GL_UNSIGNED_BYTE, _data.data());
    glBindTexture(GL_TEXTURE_2D, 0); // unbind
}